oc 协议 回调 静态成员 |
您所在的位置:网站首页 › oc 协议 › oc 协议 回调 静态成员 |
1.分类Category的使用
// 给MJPerson类添加分类
@interface MJPerson : NSObject
- (void)run;
@end
@implementation MJPerson
- (void)abc{}
- (void)run{NSLog(@"MJPerson - run");}
+ (void)run2{}
@end
// MJPerson的分类,给MJPerson添加test方法
@interface MJPerson (Test)
- (void)test;
@end
@implementation MJPerson (Test)
- (void)run{ NSLog(@"MJPerson (Test) - run");}
- (void)test{ NSLog(@"test");} // 会存放在类对象中
+ (void)test2 {} // 会存放在元类对象中
@end
// 调用拓展出来的方法
MJPerson *person = [[MJPerson alloc] init];
[person run];
[person test];
2.Category的本质
编译时会把Category数据(实例|类方法、属性、协议)放到一个category_t的结构体内。在程序运行的时候,通过Runtime将Category的数据合并到类信息(类对象、元类对象)细节:运行时候,通过Runtime加载某个类的所有Category数据,把所有Category数据,合并到一个数组中(后面参与编译的Category数据,会放在数组的前面)。然后将合并后的分类数据,插入到类原来类数据的前面(所以当category和类的方法重名的时候,会调用Category方法)。
Category与extension本质的区别 Extension在编译的时候,它的数据包含在类信息中。Category是在运行的时候,才会将数据合并到类信息中。+load方法(+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用) +load方法会在runtime加载类、分类时调用。每个类、分类的+load,在程序运行过程中只调用一次。其调用顺序如下: .先调用类的+load,按照编译先后顺序调用(先编译,先调用,调用子类的+load之前会先调用父类的+load).再调用分类的+load,按照编译先后顺序调用(先编译,先调用)注意:主动调用load方法,会和消息发送机制一样,会调到分类里面的load方法。 +Initialize方法 +initialize方法会在类第一次接收到消息时调用,调用顺序:先调用父类的+initialize,再调用子类的+initialize (先初始化父类,再初始化子类,每个类只会初始化1次)+initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,是在类第一次接收到消息的时候调用。而load是根据函数地址直接调用,实在runtime加载类、分类的时候调用,且只调用一次。 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次;调用子类,必先调用父类+initialize(第一次),然后调用这个子类的+initialize,但是这个子类没有实现,那么调用父类+initialize(第二次)方法。通过isa、supperclass指针找方法来调用)如果分类实现了+initialize,就覆盖类本身的+initialize调用。 2.分类添加成员变量(关联对象)在分类中添加一个属性,只会生成声明,不会生成成员变量和set/get的实现。 @interface MJPerson (Test) // 各类中添加属性 @property (assign, nonatomic) int weight; // 只会生成方法的声明 //- (void)setWeight:(int)weight; //- (int)weight; @end // 实现get set方法 来使得分类中的方法可以赋值和访问 #define MJKey [NSString stringWithFormat:@"%p", self] @implementation MJPerson (Test) NSMutableDictionary *names_; NSMutableDictionary *weights_; + (void)load { weights_ = [NSMutableDictionary dictionary]; names_ = [NSMutableDictionary dictionary]; } - (void)setName:(NSString *)name { names_[MJKey] = name; } - (NSString *)name { return names_[MJKey]; } - (void)setWeight:(int)weight { weights_[MJKey] = @(weight); } - (int)weight{ return [weights_[MJKey] intValue]; }关联对象 #import "MJPerson.h" @interface MJPerson (Test) @property (copy, nonatomic) NSString *name; @property (assign, nonatomic) int weight; @end #import "MJPerson+Test.h" #import @implementation MJPerson (Test) - (void)setName:(NSString *)name { objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)name { // 隐式参数 // _cmd == @selector(name) 只有get方法才可以这么写 return objc_getAssociatedObject(self, _cmd); } - (void)setWeight:(int)weight { objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (int)weight { // _cmd == @selector(weight) return [objc_getAssociatedObject(self, _cmd) intValue]; } // 这样就可以像调用类属性一样来调用分类里面的属性 person.name = "Lewis"关联对象本质 实现关联对象技术的核心如下: AssociationsManagerAssociationsHashMapObjectAssociationMapObjcAssociation![]() block的定义、简单使用 // 格式 返回值 (^block)(参数) = ^{ block的内容 } // 例子 void (^block)(void) = ^{ NSLog(@"this is a block") } // 调用 block()block本质 block本质上也是一个OC对象(最终继承于NSBlock->NSObject),它内部也有个isa指针,而且有函数调用地址,和捕获的变量。block是封装了函数调用以及函数调用环境的OC对象。block的底层结构如下图所示。![]() block捕获变量 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制。 ![]() 注:1>.self(局部变量)也会被捕获。2>.如果block里面使用的成员变量,那么其实捕获的是self对象。 block的类型 block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。 __NSGlobalBlock__ ( _NSConcreteGlobalBlock )__NSStackBlock__ ( _NSConcreteStackBlock )__NSMallocBlock__ ( _NSConcreteMallocBlock )存放区域如下: .text区域:主要放程序的代码.data区域:数据段一般放一些全局变量堆:一般放那些alloc出来的数据,动态分配内存的。特点是需要开发者写代码申请的。需要开发者管理内存。栈:放一些局部变量。特点是系统自动分配内存。![]() ![]() block的自动copy操作 当在ARC机制下有下列情况的时候,会自动对block进行copy操作 block作为函数返回值时将block赋值给__strong指针时block作为Cocoa API中方法名含有usingBlock的方法参数时block作为GCD API的方法参数时ARC下block属性的建议写法 @property (strong, nonatomic) void (^block)(void);@property (copy, nonatomic) void (^block)(void);对象类型的auto变量、__block变量、__block修饰的对象 当block内部访问了对象类型的auto变量时:如果block是在栈上,将不会对auto变量、__block变量和__block修饰的对象产生强引用。如果block被拷贝到堆上,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。(而__block修饰的auto变量(如int类型),则形成强引用,无弱引用)如果block从堆上移除,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的auto变量(release)而__block修饰的对象会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain) ![]() __block修饰符 __block可以用于解决block内部无法修改auto变量值的问题__block不能修饰全局变量、静态变量(static)编译器会将__block变量包装成一个对象![]() 循环引用的解决 ![]() |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |